home *** CD-ROM | disk | FTP | other *** search
- /*
- ** $RCSfile: sysinfo.c,v $
- ** $Filename: sysinfo.c $
- ** $Revision: 1.0 $
- ** $Date: 1996/08/20 15:35:37 $
- **
- ** sysinfo.library interface to sysmon.library features (version 2.2)
- ** See the SysInfo.doc autodoc file in the Executive distribution
- ** for more information about the available function calls.
- **
- ** (C) Copyright 1995-2001 by Etienne Vogt <Etienne.Vogt@obspm.fr>
- ** SysInfo API by Petri Nordlund <petrin@megabaud.fi>
- */
-
- #include <exec/libraries.h>
- #include <exec/memory.h>
- #include <exec/tasks.h>
- #include <exec/alerts.h>
- #include <exec/execbase.h>
- #include <devices/timer.h>
- #define __USE_SYSBASE
- #include <proto/exec.h>
- #include <proto/timer.h>
- #include <clib/alib_protos.h>
- #include "/sysmon.h"
- #include "/sysmon_protos.h"
- #include "/sysmon_pragmas.h"
- #include "sysinfo.h"
-
- #define CPU_USAGE_RECENT 60 /* Recent CPU Usage is last minute */
-
- static volatile ULONG RecentCPUUsed[CPU_USAGE_RECENT];
- static volatile int LastRecentCPU = 0;
- static volatile ULONG LastSecDispCount, LastSecVolTSw, LastSecInvTSw;
-
- struct SysmonBase *SysmonBase = NULL;
- struct Device *TimerBase = NULL;
- struct ExecBase *SysBase = NULL;
- static struct timerequest timereq;
- static BOOL ServerRunning = FALSE, ServerExit = FALSE;
- static struct Task *ServerTask;
- extern __far void ServerEntry(void); /* Stubs for server task */
- extern __far struct Library *SysInfoBase;
-
- int __saveds __asm __UserLibInit(register __a6 struct Library *libbase);
- int __saveds __asm __UserLibCleanup(register __a6 struct Library *libbase);
- static struct EClockVal subEClockVal(struct EClockVal e2, struct EClockVal e1);
- static __inline struct EClockVal addEClockVal(struct EClockVal e2, struct EClockVal e1);
- void __saveds SysInfoServer(void);
-
-
- /* This function is executed by ramlib as part of the library initialization process.
- We open sysmon.library, timer.device and start the server task via an assembler
- stub routine as CreateTask() does not allow us to preload A6 with the library
- base (and AddTask() is just a pain to use directly). */
-
- int __saveds __asm __UserLibInit(register __a6 struct Library *libbase)
- { int rc = 0;
-
- SysBase = *(struct ExecBase **)4;
- if (SysmonBase = (struct SysmonBase *)OpenLibrary("sysmon.library",1))
- { if (OpenDevice("timer.device",UNIT_VBLANK,(struct IORequest *)&timereq,0) == 0)
- { TimerBase = timereq.tr_node.io_Device;
- Forbid();
- SysInfoBase = libbase;
- if (ServerTask = CreateTask("SysInfo.server",18,ServerEntry,4000))
- { ServerRunning = TRUE;
- ServerTask->tc_UserData = libbase;
- }
- else rc = 1;
- Permit();
- }
- else
- { CloseLibrary((struct Library *)SysmonBase);
- Alert(AT_Recovery|AG_OpenDev|AO_TimerDev);
- rc = 1;
- }
- }
- else rc = 1;
-
- return rc;
- }
-
-
- /* This routine is called by ramlib's low memory handler on a failed memory allocation
- request if our library is no longer opened. Since we can't task switch from a low mem
- handler, we notify the server task that it should exit as soon as possible and delay
- the expunge. The task will call this again on exit to actually flush the library out. */
-
- int __saveds __asm __UserLibCleanup(register __a6 struct Library *libbase)
- { if (ServerRunning) /* If server is running, tell it to exit and delay the expunge */
- { ServerExit = TRUE;
- return 1;
- }
- else
- { if (TimerBase) CloseDevice((struct IORequest *)&timereq);
- if (SysmonBase) CloseLibrary((struct Library *)SysmonBase);
- return 0;
- }
- }
-
-
- */ This function is used to query the library about its supported features */
-
- __saveds struct SysInfo *InitSysInfo(void)
- { struct SysInfo *si;
-
- if (si = AllocMem(sizeof(struct SysInfo), MEMF_PUBLIC | MEMF_CLEAR))
- { si->loadavg_type = LOADAVG_FIXEDPNT;
- si->loadavg_time1 = 60;
- si->loadavg_time2 = 5*60;
- si->loadavg_time3 = 15*60;
- si->fscale = 60;
- si->cpu_usage_implemented = CPU_USAGEF_TOTAL_IMPLEMENTED |
- CPU_USAGEF_LASTSEC_IMPLEMENTED | CPU_USAGEF_RECENT_IMPLEMENTED |
- CPU_USAGEF_TOTALCSW_IMPLEMENTED | CPU_USAGEF_IVVOCSW_IMPLEMENTED |
- CPU_USAGEF_IVVOCSW_LASTSEC_IMPLEMENTED | CPU_USAGEF_TOTALCSW_LASTSEC_IMPLEMENTED;
- si->task_cpu_usage_implemented = TASK_CPU_USAGEF_TOTAL_IMPLEMENTED |
- TASK_CPU_USAGEF_TOTALCSW_IMPLEMENTED | TASK_CPU_USAGEF_IVVOCSW_IMPLEMENTED;
- }
-
- return si;
- }
-
-
- */ The function cleans up whatever was allocated by InitSysInfo() */
-
- __asm __saveds void FreeSysInfo(register __a0 struct SysInfo *si)
- { if (si) FreeMem(si, sizeof(struct SysInfo));
- }
-
-
- */ This function computes Load Averages from the data collected by sysmon.library's
- VBLANK interrupt server */
-
- __asm __saveds void GetLoadAverage(register __a0 struct SysInfo *si, register __a1 struct SI_LoadAverage *la)
- { UWORD bufpos = SysmonBase->sb_LdAvrPtr;
- ULONG total1=0, total2=0, total3=0;
- UWORD count=0;
- UBYTE bufval;
-
- do
- { bufval = SysmonBase->sb_LdAvrBuffer[bufpos];
- if (count < 60) total1 += bufval;
- if (count < 5*60) total2 += bufval;
- total3 += bufval;
-
- if (bufpos == 0) bufpos = 15*60;
- } while (bufpos--, count++, count < 15*60);
-
- la->lavg_fixed.load1 = total1;
- la->lavg_fixed.load2 = total2 / 5;
- la->lavg_fixed.load3 = total3 / 15;
- }
-
-
- /* This function returns the Process ID associated to a task. Since we don't
- support PIDs, we just return the TCB address. */
-
- __asm __saveds LONG GetPid(register __a0 struct SysInfo *si)
- { return (LONG)FindTask(NULL);
- }
-
-
- /* This function returns the Process ID of the task's parent. We don't have
- this information available, so we return an error condition. */
-
- __asm __saveds LONG GetPpid(register __a0 struct SysInfo *si)
- { return -1L;
- }
-
-
- /* This function returns the Process group number associated to a task. Since
- we don't support process groups, we return an error condition. */
-
- __asm __saveds LONG GetPgrp(register __a0 struct SysInfo *si)
- { return -1L;
- }
-
-
- /* This function returns the so called nice value of a task. We don't support
- such unixisms, so you will get an error */
-
- __asm __saveds LONG GetNice(register __a0 struct SysInfo *si, register __d0 LONG which, register __d1 LONG who)
- { si->errno = WHICH_EINVAL;
- return -1L;
- }
-
-
- /* This function sets the so called nice value of a task. We don't support
- such unixisms, so you will get an error */
-
- __asm __saveds LONG SetNice(register __a0 struct SysInfo *si, register __d0 LONG which, register __d1 LONG who, register __d2 LONG nice)
- { si->errno = WHICH_EINVAL;
- return -1L;
- }
-
-
- /* This function allows you to request a notification when internal stats have
- been updated. This is currently not supported. */
-
- __asm __saveds struct SI_Notify *AddNotify(register __a0 struct SysInfo *si, register __d0 WORD flags, register __d1 LONG safety_limit)
- { return NULL;
- }
-
-
- /* This function removes a notification request. Since notifications are currently
- not supported, there is nothing to do here. */
-
- __asm __saveds void RemoveNotify(register __a0 struct SysInfo *si, register __a1 struct SI_Notify *notify)
- {
- }
-
-
- /* This function returns statistics about CPU usage and task switches gathered by the
- server task every second. */
-
- __asm __saveds void GetCpuUsage(register __a0 struct SysInfo *si, register __a1 struct SI_CpuUsage *usage)
- { struct EClockVal uptime;
- ULONG clockrate, RecentCPU=0;
- int i;
-
- clockrate = ReadEClock(&uptime);
- usage->total_used_cputime = (SysmonBase->sb_CPUTime.ev_hi << 20 | SysmonBase->sb_CPUTime.ev_lo >> 12) / (clockrate >> 12);
- usage->total_elapsed_time = (uptime.ev_hi << 20 | uptime.ev_lo >> 12) / (clockrate >> 12);
- usage->total_csw = SysBase->DispCount;
- usage->voluntary_csw = SysmonBase->sb_VolTSw;
- usage->involuntary_csw = SysmonBase->sb_InvTSw;
-
- Forbid();
- i = (LastRecentCPU == 0 ? CPU_USAGE_RECENT-1 : LastRecentCPU-1);
- usage->used_cputime_lastsec = RecentCPUUsed[i];
- for (i = 0; i < CPU_USAGE_RECENT; i++) RecentCPU += RecentCPUUsed[i];
- usage->recent_used_cputime = RecentCPU / CPU_USAGE_RECENT;
- usage->involuntary_csw_lastsec = LastSecInvTSw;
- usage->voluntary_csw_lastsec = LastSecVolTSw;
- usage->total_csw_lastsec = LastSecDispCount;
- Permit();
-
- usage->used_cputime_lastsec_hz = clockrate;
- usage->recent_used_cputime_hz = clockrate;
- usage->recent_seconds = CPU_USAGE_RECENT;
- }
-
-
- /* This function returns total CPU usage and task switches for a given task
- as maintained by sysmon.library. No last second or recent information is
- maintained as this would be CPU expensive. */
-
- __asm __saveds LONG GetTaskCpuUsage(register __a0 struct SysInfo *si, register __a1 struct SI_TaskCpuUsage *usage, register __a2 struct Task *task)
- { struct TaskInfo *tinfo;
- struct EClockVal uptime, elapsed;
- ULONG clockrate;
-
- clockrate = ReadEClock(&uptime) >> 12;
- if (tinfo = smGetTaskInfo(task))
- { usage->total_used_cputime = tinfo->ti_CPUTime.ev_hi << 20 | tinfo->ti_CPUTime.ev_lo >> 12;
- usage->total_used_time_hz = clockrate;
- elapsed = subEClockVal(uptime, tinfo->ti_StartTime);
- usage->total_elapsed_time = (elapsed.ev_hi << 20 | elapsed.ev_lo >> 12) / clockrate;
- usage->total_csw = tinfo->ti_DispCount;
- usage->voluntary_csw = tinfo->ti_VolTSw;
- usage->involuntary_csw = tinfo->ti_InvTSw;
- return 0;
- }
- else
- { si->errno = WHICH_EINVAL;
- return 1;
- }
- }
-
-
- /* Some functions to add/subtract 64bit EClockVals */
-
- static __inline struct EClockVal subEClockVal(struct EClockVal e2, struct EClockVal e1)
- { BOOL carry = (e1.ev_lo > e2.ev_lo);
-
- e2.ev_lo -= e1.ev_lo;
- e2.ev_hi -= e1.ev_hi;
- if (carry) e2.ev_hi -= 1;
- return e2;
- }
-
- static __inline struct EClockVal addEClockVal(struct EClockVal e2, struct EClockVal e1)
- { BOOL carry = (e2.ev_lo + e1.ev_lo < e2.ev_lo);
-
- e2.ev_lo += e1.ev_lo;
- e2.ev_hi += e1.ev_hi;
- if (carry) e2.ev_hi += 1;
- return e2;
- }
-
- /* The SysInfo.server task that will track CPU Usage and task switches every second
- Note that we need to start this via an assembler stub routine as SAS/C libcode
- option assumes the library base to be in A6 at function entry */
-
- void __saveds SysInfoServer(void)
- { struct MsgPort *timerport;
- struct Library *libbase = (struct Library *)FindTask(NULL)->tc_UserData;
- struct EClockVal LastCPUUsage;
- ULONG LastDispCount, LastVolTSw, LastInvTSw;
-
- Forbid();
- LastCPUUsage = SysmonBase->sb_CPUTime;
- LastDispCount = SysBase->DispCount;
- LastVolTSw = SysmonBase->sb_VolTSw;
- LastInvTSw = SysmonBase->sb_InvTSw;
- Permit();
-
- if (timerport = CreateMsgPort())
- { timereq.tr_node.io_Message.mn_ReplyPort = timerport;
- do
- { timereq.tr_time.tv_secs = 1;
- timereq.tr_time.tv_micro = 0;
- smScheduleWakeUp(&timereq);
- do
- { smHibernate();
- } while (GetMsg(timerport) != (struct Message *)&timereq);
-
- Forbid(); /* Compute and store last second CPU Usage and task switches */
- RecentCPUUsed[LastRecentCPU++] = subEClockVal(SysmonBase->sb_CPUTime,LastCPUUsage).ev_lo;
- LastSecDispCount = SysBase->DispCount - LastDispCount;
- LastSecVolTSw = SysmonBase->sb_VolTSw - LastVolTSw;
- LastSecInvTSw = SysmonBase->sb_InvTSw - LastInvTSw;
- LastCPUUsage = SysmonBase->sb_CPUTime;
- LastDispCount = SysBase->DispCount;
- LastVolTSw = SysmonBase->sb_VolTSw;
- LastInvTSw = SysmonBase->sb_InvTSw;
- Permit();
-
- if (LastRecentCPU == CPU_USAGE_RECENT) LastRecentCPU = 0;
- } while (!ServerExit);
-
- DeleteMsgPort(timerport);
- }
-
- Forbid();
- ServerRunning = FALSE;
- if (libbase->lib_OpenCnt == 0 && libbase->lib_Flags & LIBF_DELEXP)
- { libbase->lib_OpenCnt++; /* Fake a last opener */
- CloseLibrary(libbase); /* LibClose will do the delayed expunge */
- }
- }
-